# This patch file was generated by NetBeans IDE
# This patch can be applied using context Tools: Apply Diff Patch action on respective folder.
# It uses platform neutral UTF-8 encoding.
# Above lines and this line are ignored by the patching process.
Index: moodle/admin/settings/mnet.php
--- moodle/admin/settings/mnet.php Base (1.12)
+++ moodle/admin/settings/mnet.php Locally Modified (Based On 1.12)
@@ -1,5 +1,5 @@
 <?php
-
+require_once($CFG->dirroot.'/mnet/lib.php');
 // This file defines settingpages and externalpages under the "mnet" category
 
 if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
Index: moodle/lang/en_utf8/ratings.php
--- moodle/lang/en_utf8/ratings.php No Base Revision
+++ moodle/lang/en_utf8/ratings.php Locally New
@@ -0,0 +1,8 @@
+<?php
+$string['rating'] = 'Rating';
+$string['ratings'] = 'Ratings';
+$string['norate'] = 'Rating of items not allowed!';
+$string['noviewrate'] = 'You do not have the capability to view post ratings';
+$string['noviewanyrate'] = 'You can only look at results for posts that you made';
+$string['sessionexpired'] = 'Your session has expired. Please log in again.';
+$string['ratepermissiondenied'] = 'You do not have permission to rate this item';
Index: moodle/lib/db/access.php
--- moodle/lib/db/access.php Base (1.120)
+++ moodle/lib/db/access.php Locally Modified (Based On 1.120)
@@ -1530,5 +1530,30 @@
         'legacy' => array(
             'admin' => CAP_ALLOW
         )
+    ),
+    'moodle/ratings:view' => array(
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_SYSTEM,
+        'legacy' => array(
+            'admin' => CAP_ALLOW
     )
+    ),
+    'moodle/ratings:viewall' => array(
+
+        'riskbitmask' => RISK_PERSONAL,
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_SYSTEM,
+        'legacy' => array(
+            'admin' => CAP_ALLOW
+        )
+    ),
+    'moodle/ratings:rate' => array(
+
+        'riskbitmask' => RISK_DATALOSS,
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_SYSTEM,
+        'legacy' => array(
+            'admin' => CAP_ALLOW
+        )
+    )
 );
Index: moodle/lib/db/install.xml
--- moodle/lib/db/install.xml Base (1.231)
+++ moodle/lib/db/install.xml Locally Modified (Based On 1.231)
@@ -2374,7 +2374,7 @@
         <KEY NAME="blogid" TYPE="foreign" FIELDS="blogid" REFTABLE="post" REFFIELDS="id" PREVIOUS="contextid"/>
       </KEYS>
     </TABLE>
-    <TABLE NAME="blog_external" COMMENT="External blog links used for RSS copying of blog entries to Moodle user blogs" PREVIOUS="blog_association">
+    <TABLE NAME="blog_external" COMMENT="External blog links used for RSS copying of blog entries to Moodle user blogs" PREVIOUS="blog_association" NEXT="ratings">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="userid"/>
         <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="id" NEXT="name"/>
@@ -2391,5 +2391,20 @@
         <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" PREVIOUS="primary"/>
       </KEYS>
     </TABLE>
+    <TABLE NAME="ratings" COMMENT="moodle ratings" PREVIOUS="blog_external">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="contextid"/>
+        <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="id" NEXT="itemid"/>
+        <FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="scaleid"/>
+        <FIELD NAME="scaleid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="itemid" NEXT="rating"/>
+        <FIELD NAME="rating" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="scaleid" NEXT="userid"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="rating" NEXT="timecreated"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="userid" NEXT="timemodified"/>
+        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="timecreated"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+      </KEYS>
+    </TABLE>
   </TABLES>
 </XMLDB>
Index: moodle/lib/db/upgrade.php
--- moodle/lib/db/upgrade.php Base (1.368)
+++ moodle/lib/db/upgrade.php Locally Modified (Based On 1.368)
@@ -3051,6 +3051,68 @@
         upgrade_main_savepoint($result, 2010021800);
     }
 
+    if ($result && $oldversion < 2010030800) {
+        //create the ratings table (replaces module specific ratings implementations)
+        $table = new xmldb_table('ratings');
+
+    /// Adding fields to table ratings
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+
+        $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        $table->add_field('scaleid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        $table->add_field('rating', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        
+        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+
+    /// Adding keys to table ratings
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->add_key('contextid', XMLDB_KEY_FOREIGN, array('contextid'), 'context', array('id'));
+        $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
+
+    /// Adding indexes to table ratings
+        $table->add_index('itemid', XMLDB_INDEX_NOTUNIQUE, array('itemid'));
+
+    /// Create table for ratings
+        $dbman->create_table($table);
+
+        //migrate ratings out of the modules into the central ratings table
+
+        //migrate forumratings
+        //forum ratings only have a single time column so use it for both time created and modified
+        $ratingssql = 'select id, post as itemid, rating, userid, time as timecreated, time as timemodified from {forum_ratings}';
+        $moduleandscalesql = "select f.scale, f.id from {forum} f inner join {forum_discussions} d on f.id=d.forum
+            inner join {forum_posts} p on d.id=p.discussion
+            inner join {forum_ratings} r on p.id=r.post and r.id = :rid";
+        echo "migrating forum ratings<br>";
+        $result = $result && upgrade_module_ratings($ratingssql,$moduleandscalesql,'forum');
+        
+        //migrate glossary_ratings
+        //glossary ratings only have a single time column so use it for both time created and modified
+        $ratingssql = 'select id, entryid as itemid, rating, userid, time as timecreated, time as timemodified from {glossary_ratings}';
+        $moduleandscalesql = "select g.id, g.scale
+                from {glossary} g inner join {glossary_entries} ge on g.id=ge.glossaryid
+                inner join {glossary_ratings} r on ge.id=r.entryid  and r.id = :rid";
+        echo "migrating glossary ratings<br>";
+        $result = $result && upgrade_module_ratings($ratingssql,$moduleandscalesql,'glossary');
+        
+        //migrate data_ratings
+        //data ratings didnt store time created and modified so Im using the times from the record the rating was attached to
+        $ratingssql = 'select r.id, recordid as itemid, rating, r.userid, re.timecreated, re.timemodified from {data_ratings} r inner join {data_records} re on r.recordid=re.id';
+        $moduleandscalesql = "select d.id, d.scale
+                from {data} d inner join {data_records} dr on d.id=dr.dataid
+                inner join {data_ratings} r on dr.id=r.recordid  and r.id = :rid";
+        echo "migrating data ratings<br>";
+        $result = $result && upgrade_module_ratings($ratingssql,$moduleandscalesql,'data');
+
+        upgrade_main_savepoint($result, 2010030800);
+
+        //todo andrew alter the structure of the tables forum, data and glossary if necessary
+        //todo andrew drop forum_ratings, data_ratings and glossary_ratings
+    }
+
     return $result;
 }
 
Index: moodle/lib/db/upgradelib.php
--- moodle/lib/db/upgradelib.php Base (1.30)
+++ moodle/lib/db/upgradelib.php Locally Modified (Based On 1.30)
@@ -396,3 +396,55 @@
 
     return ($outcome1 && $outcome2 && $outcome4 && $outcome4);
 }
+
+function upgrade_module_ratings($ratingssql, $moduleandscalesql, $modulename) {
+    global $DB;
+    $contextid = $moduleinstanceid = $scaleid = null;
+    $contextarray = array();
+    $result = true;
+    $i=0;
+
+    $ratings = $DB->get_records_sql($ratingssql);
+
+    foreach ($ratings as $old_rating) {
+        if($i++%500==0) {
+            upgrade_set_timeout(60);//prevent a timeout
+        }
+        //get the rating's scale and forum (or whatever) id
+        //We could cache the scale but we'd have to do a query to find out what forum the rating is part of anyway
+        $params = array();
+        $params['rid'] = $old_rating->id;
+        $results = $DB->get_record_sql($moduleandscalesql, $params);
+        $moduleinstanceid = $results->id;
+        $scaleid = $results->scale;
+
+        //all posts within a given forum will have the same context id so store them in an array
+        if( !array_key_exists($moduleinstanceid, $contextarray) ) {
+            //$sql = 'select cxt.id from {course_modules} cm inner join
+            //{context} cxt on cxt.instanceid=cm.id
+            //where cm.module=:moduleid and cm.instance=:moduleinstanceid and cxt.contextlevel='.CONTEXT_MODULE;
+            $sql = 'select cxt.id from {course_modules} cm inner join {modules} m on cm.module=m.id
+inner join {context} cxt on cxt.instanceid=cm.id
+where m.name=:modulename and cm.instance=:moduleinstanceid and cxt.contextlevel='.CONTEXT_MODULE;
+            $params = array();
+            $params['moduleinstanceid'] = $moduleinstanceid;
+            $params['modulename'] = $modulename;
+            $results = $DB->get_record_sql($sql, $params);
+            $contextarray[$moduleinstanceid] = $results->id;
+        }
+        $contextid = $contextarray[$moduleinstanceid];
+
+        $rating = new stdclass;
+        $rating->contextid = $contextid;
+        $rating->scaleid = $scaleid;
+        $rating->itemid = $old_rating->itemid;
+        $rating->rating = $old_rating->rating;
+        $rating->userid = $old_rating->userid;
+        $rating->timecreated = $old_rating->timecreated;
+        $rating->timemodified = $old_rating->timemodified;
+
+        $result = $result && $DB->insert_record('ratings', $rating);
+    }
+
+    return $result;
+}
Index: moodle/lib/outputrenderers.php
--- moodle/lib/outputrenderers.php Base (1.161)
+++ moodle/lib/outputrenderers.php Locally Modified (Based On 1.161)
@@ -1229,6 +1229,114 @@
         return html_writer::empty_tag('img', $attributes);
     }
 
+    /**
+    * Produces the html that represents this rating in the UI
+    * @param $page the page object on which this rating will appear
+    */
+    function render_rating(rating $rating) {
+        global $CFG, $USER, $PAGE;
+        static $strrate;//holds the string "rate". Its static so we only fetch it once.
+        static $havesetupjavascript = false;
+
+        if( !$havesetupjavascript && !empty($CFG->enableajax) ) {
+            $PAGE->requires->js_init_call('M.core_ratings.init');
+            $havesetupjavascript = true;
+        }
+
+        if (empty($strrate)) {
+            $strrate = get_string("rate", "forum");
+        }
+
+        $strratings = '';
+
+        if($rating->settings->permissions[RATING_VIEW] || $rating->settings->permissions[RATING_VIEW_ALL]) {
+            switch ($rating->settings->aggregationmethod) {
+                case RATING_AGGREGATE_AVERAGE :
+                    $strratings .= get_string("aggregateavg", "forum");
+                    break;
+                case RATING_AGGREGATE_COUNT :
+                    $strratings .= get_string("aggregatecount", "forum");
+                    break;
+                case RATING_AGGREGATE_MAXIMUM :
+                    $strratings .= get_string("aggregatemax", "forum");
+                    break;
+                case RATING_AGGREGATE_MINIMUM :
+                    $strratings .= get_string("aggregatemin", "forum");
+                    break;
+                case RATING_AGGREGATE_SUM :
+                    $strratings .= get_string("aggregatesum", "forum");
+                    break;
+            }
+
+            if (empty($strratings)) {
+                $strratings .= $strrate;
+            }
+            $strratings .= ': ';
+
+            $scalemax = 0;
+            $ratingstr = null;
+
+            if ( is_array($rating->settings->scale->scaleitems) ) {
+                $scalemax = $rating->settings->scale->scaleitems[ count($rating->settings->scale->scaleitems)-1 ];
+                $ratingstr = $rating->settings->scale->scaleitems[$rating->rating];
+            }
+            else { //its numeric
+                $scalemax = $rating->settings->scale->scaleitems;
+                $ratingstr = round($rating->aggregate,1);
+            }
+            
+            $aggstr = "{$ratingstr} / $scalemax ({$rating->count}) ";
+
+            if ($rating->settings->permissions[RATING_VIEW_ALL]) {
+                $link = new moodle_url("/lib/ratings/report.php?contextid={$rating->context->id}&itemid={$rating->itemid}&scaleid={$rating->scaleid}");
+                $action = new popup_action('click', $link, 'ratings', array('height' => 400, 'width' => 600));
+                $strratings .= $this->action_link($link, $aggstr, $action);
+            } else if ($rating->settings->permissions[RATING_VIEW_ALL]) {
+                $strratings .= $aggstr;
+            }
+        }
+
+        $formstart = null;
+        if($rating->settings->permissions[RATING_POST]) {
+            //dont use $rating->userid below as it will be null if the user hasnt already rated the item
+            $formstart = <<<END
+<form id="postrating{$rating->itemid}" class="postratingform" method="post" action="lib/ratings/rate.php">
+<div class="ratingform">
+<input type="hidden" class="ratinginput" name="contextid" value="{$rating->context->id}" />
+<input type="hidden" class="ratinginput" name="itemid" value="{$rating->itemid}" />
+<input type="hidden" class="ratinginput" name="scaleid" value="{$rating->settings->scale->id}" />
+<input type="hidden" class="ratinginput" name="returnurl" value="{$rating->settings->returnurl}" />
+END;
+            $strratings = $formstart.$strratings;
+
+            //generate an array of values for numeric scales
+            $scalearray = $rating->settings->scale->scaleitems;
+            if( !is_array($scalearray) && is_int($scalearray) ) {
+                $scalearray = array();
+                for($i=0; $i<=$rating->settings->scale->scaleitems; $i++) {
+                    $scalearray[$i] = $i;
+                }
+            }
+            
+            $scalearray = array(RATING_UNSET_RATING => $strrate.'...') + $scalearray;
+            $strratings .= html_writer::select($scalearray, 'rating'.$rating->itemid, $rating->rating, false, array('class'=>'postratingmenu ratinginput'));
+
+            //output submit button
+            $strratings .= '<span class="ratingsubmit"><input type="submit" class="postratingmenusubmit" id="postratingsubmit'.$rating->itemid.'" value="'.get_string('rate', 'forum').'" />';
+
+            //ajax code is included by rating::load_ratings()
+
+            if ( is_array($rating->settings->scale) ) {
+                //todo andrew where can we get the course id from?
+                //$strratings .= $this->help_icon_scale($course->id, $scale);
+                $strratings .= $this->help_icon_scale(1, $rating->settings->scale);
+            }
+            $strratings .= '</span></div></form>';
+        }
+
+        return $strratings;
+    }
+
     /*
      * Centered heading with attached help button (same title text)
      * and optional icon attached
Index: moodle/lib/outputrequirementslib.php
--- moodle/lib/outputrequirementslib.php Base (1.14)
+++ moodle/lib/outputrequirementslib.php Locally Modified (Based On 1.14)
@@ -398,6 +398,11 @@
                                     'fullpath' => '/group/module.js',
                                     'requires' => array('node', 'overlay', 'event-mouseenter'));
                     break;
+                case 'core_ratings':
+                    $module = array('name'     => 'core_ratings',
+                                    'fullpath' => '/lib/ratings/module.js',
+                                    'requires' => array('node', 'event', 'overlay', 'io', 'json'));
+                    break;
             }
 
         } else {
Index: moodle/lib/ratings/module.js
--- moodle/lib/ratings/module.js No Base Revision
+++ moodle/lib/ratings/module.js Locally New
@@ -0,0 +1,58 @@
+M.core_ratings={
+
+    Y : null,
+    transaction : [],
+
+    init : function(Y){
+        this.Y = Y;
+        Y.all('select.postratingmenu').each(this.attach_rating_events, this);
+
+        //hide the submit buttons
+        this.Y.all('input.postratingmenusubmit').setStyle('display', 'none');
+    },
+
+    attach_rating_events : function(selectnode) {
+        selectnode.on('change', this.submit_rating, this, selectnode);
+    },
+
+    submit_rating : function(e, selectnode){
+        var theinputs = selectnode.ancestor('form').all('.ratinginput')
+        var thedata = [];
+
+        var inputssize = theinputs.size();
+        for ( var i=0; i<inputssize; i++ )
+        {
+            if(theinputs.item(i).get("name")!="returnurl") {//dont include return url for ajax requests
+                thedata[theinputs.item(i).get("name")] = theinputs.item(i).get("value");
+            }
+        }
+        
+        this.Y.io.queue.stop();
+        this.transaction.push({transaction:this.Y.io.queue(M.cfg.wwwroot+'/lib/ratings/rate.php', {
+            method : 'POST',
+            data : build_querystring(thedata),
+            on : {
+                complete : function(tid, outcome, args) {
+                    try {
+                        outcome = this.Y.JSON.parse(outcome.responseText);
+                    } catch(e) {
+                        //this.form.submit();
+                        alert(outcome.responseText);
+                    }
+                    if(outcome.success){
+                        //do nothing
+                    }
+                    else if (outcome.error){
+                        //todo andrew put up an overlay or similar rather than an alert
+                        alert(outcome.error);
+                    }
+                }
+            },
+            context : this,
+            arguments : {
+                //query : this.query.get('value')
+            }
+        }),complete:false,outcome:null});
+        this.Y.io.queue.start();
+    }
+}
\ No newline at end of file
Index: moodle/lib/ratings/rate.php
--- moodle/lib/ratings/rate.php No Base Revision
+++ moodle/lib/ratings/rate.php Locally New
@@ -0,0 +1,111 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This page receives rating submissions
+ *
+ * This page can be the target for either ajax or non-ajax rating submissions.
+ * If a return url is supplied the request is presumed to be a non-ajax request so a page
+ * is returned.
+ * If there is no return url the request is presumed to be ajax so a json response is returned.
+ *
+ * @package   moodlecore
+ * @copyright 2010 Andrew Davis
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+if (!file_exists('../../config.php')) {
+    header('Location: ../../install.php');
+    die;
+}
+
+require_once('../../config.php');
+//todo andrew do we need these includes?
+//require_once($CFG->dirroot .'/course/lib.php');
+//require_once($CFG->libdir .'/filelib.php');
+
+require_once('ratinglib.php');
+
+$result = new stdClass;
+
+if( !isloggedin() ){ //session has expired
+    $result->error = get_string('sessionexpired', 'ratings');
+    echo json_encode($result);
+    die();
+}
+
+$contextid = required_param('contextid', PARAM_INT);
+$itemid = required_param('itemid', PARAM_INT);
+$scaleid = required_param('scaleid', PARAM_INT);
+
+$context = get_context_instance_by_id($contextid);
+var_dump($context);
+$cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST);
+var_dump($cm);die();
+$courseid = $cm->course;
+var_dump($courseid);die();
+//$course = get_course_from_path($context->path);
+require_login($courseid, false, $cm);
+
+//$userid = required_param('userid', PARAM_INT);
+$userid = $USER->id;
+
+$userrating = required_param('rating'.$itemid, PARAM_INT);
+$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);//will only be supplied for non-ajax requests
+
+$PAGE->set_url('/lib/rate.php', array(
+        'contextid'=>$contextid,
+        'itemid'=>$itemid,
+        'scaleid'=>$scaleid,
+        'rating'=>$userrating,
+        'userid'=>$userid,
+        'returnurl'=>$returnurl,
+    ));
+
+if( $returnurl ) {
+    //echo $OUTPUT->header();
+}
+
+$permissions = rating::get_rating_permissions($context);
+if( !$permissions[RATING_POST] ) {
+    echo get_string('ratepermissiondenied', 'ratings');
+}
+
+//todo andrew validate the forum,glossary or whatever and the item id
+//how do we know where to look for the item? how we we work from module to forum_posts, glossary_entries etc.
+//if ($rating_context->contextlevel == CONTEXT_COURSE) {
+//    $courseid = $rating_context->instanceid;
+//    $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
+//if ($rating_context->contextlevel == CONTEXT_MODULE) {
+//    $cm = get_coursemodule_from_id(false, $rating_context->instanceid, 0, false, MUST_EXIST);
+//    $courseid = $cm->course;
+//}
+
+$rating = new Rating($context, $itemid, $scaleid, $userid);
+$rating->update_rating($userrating);
+
+//if its a non-ajax request
+if($returnurl) {
+    redirect($CFG->wwwroot.'/'.$returnurl);
+    //echo $OUTPUT->footer();
+}
+else { //this is an ajax request
+    $result = new stdClass;
+    $result->success = true;
+    echo json_encode($result);
+    die();
+}
\ No newline at end of file
Index: moodle/lib/ratings/ratinglib.php
--- moodle/lib/ratings/ratinglib.php No Base Revision
+++ moodle/lib/ratings/ratinglib.php Locally New
@@ -0,0 +1,312 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * A class representing a single rating and containing some static methods for manipulating ratings
+ *
+ * @package   moodlecore
+ * @copyright 2010 Andrew Davis
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+ define('RATING_VIEW','view');
+ define('RATING_VIEW_ALL','viewall');
+ define('RATING_POST','post');
+
+define('RATING_UNSET_RATING', -999);
+
+//define ('RATING_AGGREGATE_NONE', 0); //no ratings
+define ('RATING_AGGREGATE_AVERAGE', 1);
+define ('RATING_AGGREGATE_COUNT', 2);
+define ('RATING_AGGREGATE_MAXIMUM', 3);
+define ('RATING_AGGREGATE_MINIMUM', 4);
+define ('RATING_AGGREGATE_SUM', 5);
+
+/**
+ * The rating class represents a single rating by a single user. It also contains a static method to retrieve sets of ratings.
+ *
+ * @copyright 2010 Andrew Davis
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class rating implements renderable {
+
+/**
+* Constructor.
+* @param $contextid the current context
+* @param $itemid the id of the associated item (forum post, glossary item etc)
+* @param $scaleid the scale to use
+* @param $userid the user submitting the rating
+*/
+function __construct($context, $itemid, $scaleid, $userid) {
+    $this->context = $context;
+    $this->itemid = $itemid;
+    $this->scaleid = $scaleid;
+    $this->userid = $userid;
+}
+
+/**
+* Update this rating in the database
+* @param integer $rating the integer value of this rating
+*/
+function update_rating($rating) {
+    global $DB;
+
+    $data = new stdclass();
+    $table = 'ratings';
+
+    $item = new stdclass();
+    $item->id = $this->itemid;
+    $items = array($item);
+    
+    $items = rating::load_ratings($this->context, $items, null, $this->scaleid, $this->userid);
+    if( !isset($items[0]->rating) || !isset($items[0]->rating->id) ) {
+        $data->contextid    = $this->context->id;
+        $data->rating       = $rating;
+        $data->scaleid      = $this->scaleid;
+        $data->userid       = $this->userid;
+        $data->itemid       = $this->itemid;
+
+        $time = time();
+        $data->timecreated = $time;
+        $data->timemodified = $time;
+
+        $DB->insert_record($table, $data);
+    }
+    else {
+        //$data->id       = $this->id;
+        $data->id       = $items[0]->rating->id;
+        /*$data->contextid    = $this->context->id;
+        $data->scaleid      = $this->scaleid;
+        $data->userid       = $this->userid;*/
+        $data->rating       = $rating;
+
+        $time = time();
+        $data->timemodified = $time;
+
+        $DB->update_record($table, $data);
+    }
+}
+
+/**
+* Retreive the integer value of this rating
+*/
+function get_rating() {
+    return $this->rating;
+}
+
+/**
+* Remove this rating from the database
+*/
+function delete_rating() {
+    //todo implement this if its actually needed
+}
+
+/**
+* Static method that converts an aggregation method constant into something that can be included in SQL
+* @param $aggregate An aggregation constant. For example, RATING_AGGREGATE_AVERAGE.
+*/
+public static function rating_get_aggregation_method($aggregate) {
+    $aggregatestr = null;
+    switch($aggregate){
+        case RATING_AGGREGATE_AVERAGE:
+            $aggregatestr = 'AVG';
+            break;
+        case RATING_AGGREGATE_COUNT:
+            $aggregatestr = 'CNT';
+            break;
+        case RATING_AGGREGATE_MAXIMUM:
+            $aggregatestr = 'MAX';
+            break;
+        case RATING_AGGREGATE_MINIMUM:
+            $aggregatestr = 'MIN';
+            break;
+        case RATING_AGGREGATE_SUM:
+            $aggregatestr = 'SUM';
+            break;
+    }
+    return $aggregatestr;
+}
+
+/**
+* Static method that returns an array of ratings for a given item (forum post, glossary entry etc)
+ * This returns all users ratings for a single item
+* @param $context the context in which the rating exists
+* @param $itemid The id of the forum posts, glossary items or whatever
+*/
+public static function load_ratings_for_item($context, $itemid, $sort) {
+    global $DB;
+
+    $sql = "SELECT r.id, r.rating, r.itemid, r.userid, r.timemodified,
+u.firstname, u.lastname, u.imagealt, u.picture
+FROM {ratings} r
+LEFT JOIN {user} u ON r.userid = u.id
+WHERE
+    r.contextid = :contextid AND
+    r.itemid  = :itemid
+$sort";
+
+    $params['contextid'] = $context->id;
+    $params['itemid'] = $itemid;
+
+    return $DB->get_records_sql($sql, $params);
+}
+
+/**
+* Static method that adds rating objects to an array of items (forum posts, glossary entries etc)
+* Rating objects are available at $item->rating
+* @param $contextid the current context
+* @param $items an array of items such as forum posts or glossary items. They must have an 'id' member ie $items[0]->id
+* @param $aggregate what aggregation method should be applied. AVG, MAX etc
+* @param $scaleid the scale from which the user can select a rating
+* @param $userid the id of the current user
+*/
+public static function load_ratings($context, $items, $aggregate=RATING_AGGREGATE_AVERAGE, $scaleid=5, $userid = null, $returnurl = null) {
+    global $DB, $USER, $PAGE, $CFG;
+
+    if(empty($items)) {
+        return $items;
+    }
+
+    if (is_null($userid)) {
+        $userid = $USER->id;
+    }
+
+    $aggregatestr = rating::rating_get_aggregation_method($aggregate);
+
+    //create an array of item ids
+    $itemids = array();
+    foreach($items as $item) {
+        $itemids[] = $item->id;
+    }
+
+    //get the items from the database
+    list($itemidtest, $params) = $DB->get_in_or_equal(
+            $itemids, SQL_PARAMS_NAMED, 'itemid0000');
+
+    $sql = "SELECT r.itemid, ur.id, ur.userid, ur.scaleid, 
+    $aggregatestr(r.rating) AS aggrrating,
+    COUNT(r.rating) AS numratings,
+    ur.rating AS usersrating
+FROM {ratings} r
+LEFT JOIN {ratings} ur ON ur.contextid = r.contextid AND
+        ur.itemid = r.itemid AND
+        ur.userid = :userid
+WHERE
+    r.contextid = :contextid AND
+    r.itemid $itemidtest
+GROUP BY r.itemid, ur.rating
+ORDER BY r.itemid";
+
+    $params['userid'] = $userid;
+    $params['contextid'] = $context->id;
+
+    $ratingsrecords = $DB->get_records_sql($sql, $params);
+
+    //now create the rating sub objects
+    $permissions = rating::get_rating_permissions($context);
+
+    $scaleobj = new stdClass();
+    $scalemax = null;
+
+    //todo we could look for a scale id on each item to allow each item to use a different scale
+
+    if($scaleid < 0 ) { //if its a scale (not numeric)
+        $scalerecord = $DB->get_record('scale', array('id' => -$scaleid));
+        if ($scalerecord) {
+            $scaleobj->scaleitems = explode(',', $scalerecord->scale);
+            $scaleobj->id = $scalerecord->id;
+            $scaleobj->name = $scalerecord->name;
+
+            $scalemax = count($scaleobj->scale)-1;
+        }
+    }
+    else { //its numeric
+        $scaleobj->scaleitems = $scaleid;
+        $scaleobj->id = $scaleid;
+        $scaleobj->name = null;
+
+        $scalemax = $scaleid;
+    }
+
+    $settings = new stdclass(); //settings that are common to all ratings objects in this context
+    $settings->scale = $scaleobj; //the scale to use now
+    $settings->permissions = $permissions;
+    $settings->aggregationmethod = $aggregate;
+    $settings->returnurl = $returnurl;
+
+    $rating = null;
+    foreach($items as $item) {
+        $rating = null;
+        //match the item with its corresponding rating
+        foreach($ratingsrecords as $rec) {
+            if( $item->id==$rec->itemid ) {
+                //Note: rec->scaleid = the id of scale at the time the rating was submitted
+                //may be different from the current scale id
+                $rating = new rating($context, $item->id, $rec->scaleid, $rec->userid);
+                $rating->id         = $rec->id;    //unset($rec->id);
+                $rating->aggregate  = $rec->aggrrating; //unset($rec->aggrrating);
+                $rating->count      = $rec->numratings; //unset($rec->numratings);
+                $rating->rating     = $rec->usersrating; //unset($rec->usersrating);
+                break;
+            }
+        }
+        //if there are no ratings for this item
+        if( !$rating ) {
+            $scaleid = $userid = null;
+            $rating = new rating($context, $item->id, $scaleid, $userid);
+            $rating->id         = null;
+            $rating->aggregate  = null;
+            $rating->count      = 0;
+            $rating->rating     = null;
+
+            $rating->itemid     = $item->id;
+            $rating->userid     = null;
+            $rating->scaleid     = null;
+        }
+
+        $rating->settings = $settings;
+        $item->rating = $rating;
+
+        //Below is a nasty hack presumably here to handle scales being changed (out of 10 to out of 5 for example)
+        //
+        // it could throw off the grading if count and sum returned a grade higher than scale
+        // so to prevent it we review the results and ensure that grade does not exceed the scale, if it does we set grade = scale (i.e. full credit)
+        if ($rating->rating > $scalemax) {
+            $rating->rating = $scalemax;
+        }
+    }
+    return $items;
+}
+
+/**
+ * Iterate over $items (forum posts, glossary items etc) and create $item->rating, $item->rating->aggregate and $item->rating->count
+ * Similar to make_context_subobj()
+ * @param array $items array of items
+ * @param resultset $ratings resultset. probably from ratings_load_ratings()
+ */
+private static function make_rating_subobjs( $context, $items, $ratingsrecords, $aggregate, $scaleid, $returnurl) {
+    global $DB;
+
+    
+}
+
+public static function get_rating_permissions($context) {
+    return array(RATING_VIEW=>has_capability('moodle/ratings:view',$context), RATING_VIEW_ALL=>has_capability('moodle/ratings:viewall',$context), RATING_POST=>has_capability('moodle/ratings:rate',$context));
+}
+
+} //end rating class definition
\ No newline at end of file
Index: moodle/lib/ratings/report.php
--- moodle/lib/ratings/report.php No Base Revision
+++ moodle/lib/ratings/report.php Locally New
@@ -0,0 +1,103 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * A page to display a list of ratings for a given item (forum post etc)
+ *
+ * @package   moodlecore
+ * @copyright 2010 Andrew Davis
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once("../../config.php");
+require_once("ratinglib.php");
+
+$itemcontextid   = required_param('contextid', PARAM_INT);
+$itemid   = required_param('itemid', PARAM_INT);
+$scaleid   = required_param('scaleid', PARAM_INT);
+$sort = optional_param('sort', '', PARAM_ALPHA);
+
+$context = get_context_instance_by_id($itemcontextid);
+$cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST);
+$courseid = $cm->course;
+//$course = get_course_from_path($context->path);
+require_login($courseid, false, $cm);
+
+$url = new moodle_url('/lib/ratings/report.php', array('contextid'=>$itemcontextid,'itemid'=>$itemid));
+if ($sort !== 0) {
+    $url->param('sort', $sort);
+}
+$PAGE->set_url($url);
+
+$permissions = rating::get_rating_permissions(context);
+if (!$permissions[RATING_VIEW]) {
+    print_error('noviewrate', 'ratings');
+}
+if (!$permissions[RATING_VIEW_ALL] and $USER->id != $item->userid) {
+    print_error('noviewanyrate', 'ratings');
+}
+
+switch ($sort) {
+    case 'firstname': $sqlsort = "u.firstname ASC"; break;
+    case 'rating':    $sqlsort = "r.rating ASC"; break;
+    default:          $sqlsort = "r.timemodified ASC";
+}
+
+$scalemenu = make_grades_menu($scaleid);
+
+$strratings = get_string('ratings', 'ratings');
+$strrating  = get_string('rating', 'ratings');
+$strname    = get_string('name');
+$strtime    = get_string('time');
+
+//Is there something more meaningful we can put in the title?
+//$PAGE->set_title("$strratings: ".format_string($post->subject));
+$PAGE->set_title("$strratings: ".format_string($itemid));
+echo $OUTPUT->header();
+
+//if (!$ratings = forum_get_ratings($post->id, $sqlsort)) {
+$ratings = rating::load_ratings_for_item($context, $itemid, $sort);
+if (!$ratings) {
+    //print_error('noresult', 'forum', '', format_string($post->subject));
+    print_error('noresult');
+} else {
+    echo "<table border=\"0\" cellpadding=\"3\" cellspacing=\"3\" class=\"generalbox\" style=\"width:100%\">";
+    echo "<tr>";
+    echo "<th class=\"header\" scope=\"col\">&nbsp;</th>";
+    echo "<th class=\"header\" scope=\"col\"><a href=\"report.php?id=$itemid&amp;sort=firstname\">$strname</a></th>";
+    echo "<th class=\"header\" scope=\"col\" style=\"width:100%\"><a href=\"report.php?id=$itemid&amp;sort=rating\">$strrating</a></th>";
+    echo "<th class=\"header\" scope=\"col\"><a href=\"report.php?id=$itemid&amp;sort=time\">$strtime</a></th>";
+    echo "</tr>";
+    foreach ($ratings as $rating) {
+        echo '<tr class="forumpostheader">';
+        echo "<td>";
+        if($courseid) {
+            echo $OUTPUT->user_picture($rating, array('courseid'=>$courseid));
+        } else {
+            echo $OUTPUT->user_picture($rating);
+        }
+        echo '</td><td>'.fullname($rating).'</td>';
+        echo '<td style="white-space:nowrap" align="center" class="rating">'.$scalemenu[$rating->rating]."</td>";
+        echo '<td style="white-space:nowrap" align="center" class="time">'.userdate($rating->timemodified)."</td>";
+        echo "</tr>\n";
+    }
+    echo "</table>";
+    echo "<br />";
+}
+
+echo $OUTPUT->close_window_button();
+echo $OUTPUT->footer();
Index: moodle/mod/forum/lib.php
--- moodle/mod/forum/lib.php Base (1.852)
+++ moodle/mod/forum/lib.php Locally Modified (Based On 1.852)
@@ -42,8 +42,10 @@
 define('FORUM_TRACKING_OPTIONAL', 1);
 define('FORUM_TRACKING_ON', 2);
 
+//todo andrew remove this
 define('FORUM_UNSET_POST_RATING', -999);
 
+//todo andrew and remove these
 define ('FORUM_AGGREGATE_NONE', 0); //no ratings
 define ('FORUM_AGGREGATE_AVG', 1);
 define ('FORUM_AGGREGATE_COUNT', 2);
@@ -1453,6 +1455,7 @@
  * @param int $userid optional user id, 0 means all users
  * @return array array of grades, false if none
  */
+ //todo andrew pretty sure I can remove this
 function forum_get_user_grades($forum, $userid=0) {
     global $CFG, $DB;
 
@@ -4155,6 +4158,7 @@
  * @param array $scale is an array of ratings
  * @param int $myrating
  */
+ //todo andrew remove this function
 function forum_print_rating_menu($postid, $userid, $scale, $myrating=NULL) {
 
     static $strrate;
Index: moodle/sandbox-ratings.php
--- moodle/sandbox-ratings.php No Base Revision
+++ moodle/sandbox-ratings.php Locally New
@@ -0,0 +1,56 @@
+<?php
+if (!file_exists('./config.php')) {
+    header('Location: install.php');
+    die;
+}
+
+require_once('config.php');
+require_once($CFG->dirroot .'/course/lib.php');
+require_once($CFG->libdir .'/filelib.php');
+
+require_once('lib/ratings/ratinglib.php');
+
+require_login(SITEID);
+//require_login($cm->course, false, $cm);
+
+$PAGE->set_url(new moodle_url('/ratings_sandbox.php'));
+echo $OUTPUT->header();
+
+$context = get_context_instance(CONTEXT_SYSTEM);
+//$context = get_context_instance(CONTEXT_MODULE, 44);
+
+$userid = $USER->id;
+
+//settings provided by the module whose items are being rated
+$aggregate = RATING_AGGREGATE_AVERAGE; //member variable on $forum
+$scaleid = 5;//$forum->scaleid
+$returnurl = 'sandbox-ratings.php';
+
+//these items would typically be forum posts, glossary items or something like that
+$items = array();
+
+$item = new object();
+$item->id = 1;
+$items[] = $item;
+
+$item = new object();
+$item->id = 2;
+$items[] = $item;
+
+$item = new object();
+$item->id = 3;
+$items[] = $item;
+
+$items = rating::load_ratings($context, $items, $aggregate, $scaleid, $userid, $returnurl);
+
+foreach($items as $item) {
+    //begin rendering item
+    echo "<div style='padding:10px;margin:30px;background-color:#ffffcc;'>item {$item->id}";
+
+    echo $OUTPUT->render($item->rating);
+
+    //finish rendering item
+    echo "</div>";
+}
+
+echo $OUTPUT->footer();
\ No newline at end of file
Index: moodle/version.php
--- moodle/version.php Base (1.1469)
+++ moodle/version.php Locally Modified (Based On 1.1469)
@@ -6,7 +6,7 @@
 // This is compared against the values stored in the database to determine
 // whether upgrades should be performed (see lib/db/*.php)
 
-    $version = 2010021901;  // YYYYMMDD   = date of the last version bump
+    $version = 2010030800;  // YYYYMMDD   = date of the last version bump
                             //         XX = daily increments
 
     $release = '2.0 dev (Build: 20100311)';  // Human-friendly version name
